<?php
//==============================================
//PHPZip 英文函数库 v0.1 Beta版本
//
// 代码修改       黄创钦,PHPText.Net
// 代码作者       未知
// 联系方式       QQ 233944397
// 本代码开源使用，可以随意修改传播，但请保留本版权
// 声明,谢谢合作！
//==============================================

//初始化PHPZip
function PHPZip_Init(){
	if(!$GLOBALS["PHPZip_Init"])
	{
		$GLOBALS["DataSec"]      = array();
		$GLOBALS["Ctrl_Dir"]     = array();
		$GLOBALS["Eof_Ctrl_Dir"] = "\x50\x4b\x05\x06\x00\x00\x00\x00";
		$GLOBALS["Old_Offset"]   = 0;
		$GLOBALS["PHPZip_Init"]  = 1;

		return 1;
	}
	else
	{
		return 0;
	}
}

//清空数据
function PHPZip_ClearData()
{
	//还原数据内容
	$GLOBALS["DataSec"]      = array();
	$GLOBALS["Ctrl_Dir"]     = array();
	$GLOBALS["Eof_Ctrl_Dir"] = "\x50\x4b\x05\x06\x00\x00\x00\x00";
	$GLOBALS["Old_Offset"]   = 0;
}

//关闭PHPZip
function PHPZip_Close()
{
	if($GLOBALS["PHPZip_Init"])
	{
		unset($GLOBALS["DataSec"],$GLOBALS["Ctrl_Dir"],$GLOBALS["Eof_Ctrl_Dir"],$GLOBALS["Old_Offset"],$GLOBALS["Dirs"],$GLOBALS["PHPZip_Init"]);
		return 1;
	}
	else
	{
		return 0;
	}
}

//转化unix时间为dos时间
function _Unix2DosTime($Unixtime= 0)
{
	$TimeArray = ($Unixtime== 0) ? getdate() : getdate($Unixtime);

	if ($TimeArray["year"] < 1980)
	{
		$TimeArray["year"]    = 1980;
		$TimeArray["mon"]     = 1;
		$TimeArray["mday"]    = 1;
		$TimeArray["hours"]   = 0;
		$TimeArray["minutes"] = 0;
		$TimeArray["seconds"] = 0;
	}
	return (($TimeArray["year"] - 1980) << 25) | ($TimeArray["mon"] << 21) | ($TimeArray["mday"] << 16) | ($TimeArray["hours"] << 11) | ($TimeArray["minutes"] << 5) | ($TimeArray["seconds"] >> 1);
}

//读取ZIP文件头
function _ReadFileHeader($Zip)
{
	$Binary_Data = fread($Zip,30);
	$Data = unpack("vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len", $Binary_Data);

	$Headers["filename"] = fread($Zip,$Data["filename_len"]);
	if($Data["extra_len"] != 0){
		$Headers["extra"] = fread($Zip,$Data["extra_len"]);
	}else{ $Headers["extra"] = ""; }

	$Headers["compression"]     = $Data["compression"];
	$Headers["size"]            = $Data["size"];
	$Headers["compressed_size"] = $Data["compressed_size"];
	$Headers["crc"]             = $Data["crc"];
	$Headers["flag"]            = $Data["flag"];
	$Headers["mdate"]           = $Data["mdate"];
	$Headers["mtime"]           = $Data["mtime"];

	if ($Headers["mdate"] and $Headers["mtime"])
	{
		$Hour    = ($Headers["mtime"]&0xF800)>>11;
		$Minute  = ($Headers["mtime"]&0x07E0)>>5;
		$Seconde = ($Headers["mtime"]&0x001F)*2;
		$Year    = (($Headers["mdate"]&0xFE00)>>9)+1980;
		$Month   = ($Headers["mdate"]&0x01E0)>>5;
		$day     = $Headers["mdate"]&0x001F;

		$Headers["mtime"] = mktime($Hour,$Minute,$Seconde,$Month,$Day,$Year);
	}
	else
	{
		$Headers["mtime"] = time();
	}

	$Headers["stored_filename"] = $Headers["filename"];
	$Headers["status"] = "ok";
	return $Headers;
}

//读取文件内容头
function _ReadCentralFileHeaders($Zip){
	$Binary_Data = fread($Zip,46);
	$Headers     = unpack("vchkid/vid/vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset", $Binary_Data);

	if($Headers["filename_len"] != 0){
		$Headers["filename"] = fread($Zip,$Headers["filename_len"]);
	}else{ $Headers["filename"] = ""; }

	if($Headers["extra_len"] != 0){
		$Headers["extra"] = fread($Zip, $Headers["extra_len"]);
	}else{ $Headers["extra"] = ""; }

	if($Headers["comment_len"] != 0){
		$Headers["comment"] = fread($Zip, $Headers["comment_len"]);
	}else{ $Headers["comment"] = ""; }

	if($Headers["mdate"] && $Headers["mtime"]){
		$Hour    = ($Headers["mtime"] & 0xF800) >> 11;
		$Minute  = ($Headers["mtime"] & 0x07E0) >> 5;
		$Seconde = ($Headers["mtime"] & 0x001F)*2;
		$Year    = (($Headers["mdate"] & 0xFE00) >> 9) + 1980;
		$Month   = ($Headers["mdate"] & 0x01E0) >> 5;
		$Day     = $Headers["mdate"] & 0x001F;

		$Headers["mtime"] = mktime($Hour,$Minute,$Seconde,$Month,$Day,$Year);
	}else{ $Headers["mtime"] = time(); }

	$Headers["stored_filename"] = $Headers["filename"];
	$Headers["status"] = "ok";
	if(substr($Headers["filename"], -1) == "/"){ $Headers["external"] = 0x41FF0010; }

	return $Headers;
 }

//读取目录内容
function _ReadCentralDir($Zip,$ZipName){
	$Size = filesize($ZipName);
	if($Size < 277){
		$Maximum_Size = $Size;
	}else{ $Maximum_Size = 277; }

	@fseek($Zip,$Size-$Maximum_Size);
	$pos   = ftell($Zip);
	$bytes = 0x00000000;

	while($pos < $Size){
		$byte  = @fread($Zip,1);
		$bytes = ($bytes << 8) | Ord($byte);
		if($bytes == 0x504b0506){ $pos++; break; }
		$pos++;
	}

	$Data=unpack("vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size",fread($Zip,18));

	if($Data["comment_size"] != 0){
		$Centd["comment"] = fread($Zip, $Data["comment_size"]);
	}else{ $Centd["comment"] = ""; $Centd["entries"] = $Data["entries"]; }

	$Centd["disk_entries"] = $Data["disk_entries"];
	$Centd["offset"]       = $Data["offset"];
	$Centd["disk_start"]   = $Data["disk_start"];
	$Centd["size"]         = $Data["size"];
	$Centd["disk"]         = $Data["disk"];

	return $Centd;
}


//解压文件
function _ExtractFile($Headers,$ToDir,$Zip){
	//返回文件内容&属性
	$Headers = _ReadFileHeader($Zip);

	//判断目录尾是否有/存在
	if($ToDir!="" and substr($ToDir,-1)!="/"){ $ToDir.="/"; }

	//判断目录是否存在
	if($ToDir!="" and !is_dir($ToDir)){ @mkdir($ToDir,0777); }


	//获取所有子目录
	$Pth=explode("/",dirname($Headers["filename"]));

	//创建子目录完整路径
	$Pths="";

	//建立子目录
	$Pths=$Pth[0];
	if(!is_dir($ToDir.$Pths)){ @mkdir($ToDir.$Pths,0777); }
	for($i=1;isset($Pth[$i]);$i++){
		$Pths.="/".$Pth[$i];
		if(!$Pth[$i]){ break; }
 		if(!is_dir($ToDir.$Pths)){ @mkdir($ToDir.$Pths,0777); }
	}

	//建立文件
	if (!($Headers["external"]==0x41FF0010) and !($Headers["external"]==16)){
		$ZipFileName=$ToDir.$Headers["filename"];
		if($Headers["compression"]==0){
			$FileHead = @fopen($ZipFileName,"wb");
			if(!$FileHead){ return(-1); }
			$Size = $Headers["compressed_size"];

			while($Size != 0){
				$Read_Size   = ($Size > 2048) ? $Size : 2048;
				$Buffer      = fread($Zip,$Read_Size);
				$Binary_Data = pack("a".$Read_Size,$Buffer);

				@fwrite($FileHead,$Binary_Data);
				$Size -= $Read_Size;
			}
			fclose($FileHead);
			touch($ZipFileName,$Headers["mtime"]);
		}else{
			$FileHead = @fopen($ZipFileName.".gz","wb");
			if(!$FileHead){ return(-1); }
			$Binary_Data = pack("va1a1Va1a1",0x8b1f,Chr($Headers["compression"]),Chr(0x00),time(),Chr(0x00),Chr(3));

			fwrite($FileHead,$Binary_Data,10);
			$Size = $Headers["compressed_size"];

			while($Size != 0){
				$Read_Size   = ($Size < 1024) ? $Size : 1024;
				$Buffer      = fread($Zip,$Read_Size);
				$Binary_Data = pack("a".$Read_Size,$Buffer);

				@fwrite($FileHead,$Binary_Data,$Read_Size);
				$Size -= $Read_Size;
			}

			$Binary_Data = pack("VV", $Headers["crc"], $Headers["size"]);
			fwrite($FileHead, $Binary_Data,8);
			fclose($FileHead);

			$Gzp = @gzopen($ZipFileName.".gz","rb");
			if(!$Gzp){ return(-2); }
			$FileHead = @fopen($ZipFileName,"wb");
			if(!$FileHead){ return(-1); }
			$Size = $Headers["size"];

			while($Size != 0){
				$Read_Size = ($Size < 2048) ? $Size : 2048;
				$Buffer = gzread($Gzp,$Read_Size);
				$Binary_Data = pack("a".$Read_Size,$Buffer);
				@fwrite($FileHead,$Binary_Data,$Read_Size);
				$Size -= $Read_Size;
			}
			fclose($FileHead);
			gzclose($Gzp);

			touch($ZipFileName,$Headers["mtime"]);
			@unlink($ZipFileName.".gz");
		}
	}
  	return true;
}

//增加压缩文件
function PHPZip_AddFile($FileData,$FileName,$Times = 0){
	$FileName     = str_replace("\\","/",$FileName);

	$DTime        = dechex(_Unix2DosTime($Times));
	$HexdTime     = "\x" . $DTime[6] . $DTime[7] . "\x" . $DTime[4] . $DTime[5] . "\x" . $DTime[2] . $DTime[3] . "\x" . $DTime[0] . $DTime[1];
	eval('$HexdTime = "' . $HexdTime . '";');

	$Fr    = "\x50\x4b\x03\x04";
	$Fr   .= "\x14\x00";
	$Fr   .= "\x00\x00";
	$Fr   .= "\x08\x00";
	$Fr   .= $HexdTime;

	//本地文件头信息
	$UNC_Len = strlen($FileData);
	$CRC     = crc32($FileData);
	$ZData   = gzcompress($FileData);
	$C_Len   = strlen($ZData);
	$ZData   = substr(substr($ZData,0,strlen($ZData) - 4), 2);
	$Fr     .= pack("V", $CRC);
	$Fr     .= pack("V", $C_Len);
	$Fr     .= pack("V", $UNC_Len);
	$Fr     .= pack("v", strlen($FileName));
	$Fr     .= pack("v", 0);
	$Fr     .= $FileName;

	//文件数据
	$Fr .= $ZData;

	//文件描述
	$Fr .= pack("V", $CRC);
	$Fr .= pack("V", $C_Len);
	$Fr .= pack("V", $UNC_Len);

	//增加文件信息到压缩数据
	$GLOBALS["DataSec"][] = $Fr;
	$New_Offset	      = strlen(implode("",$GLOBALS["DataSec"]));

	//加入压缩目录数据
	$Cdrec  = "\x50\x4b\x01\x02";
	$Cdrec .= "\x00\x00";
	$Cdrec .= "\x14\x00";
	$Cdrec .= "\x00\x00";
	$Cdrec .= "\x08\x00";
	$Cdrec .= $HexdTime;
	$Cdrec .= pack("V", $CRC);
	$Cdrec .= pack("V", $C_Len);
	$Cdrec .= pack("V", $UNC_Len);
	$Cdrec .= pack("v", strlen($FileName));
	$Cdrec .= pack("v", 0);
	$Cdrec .= pack("v", 0);
	$Cdrec .= pack("v", 0);
	$Cdrec .= pack("v", 0);
	$Cdrec .= pack("V", 32);
	$Cdrec .= pack("V", $GLOBALS["Old_Offset"]);

	$GLOBALS["Old_Offset"]  = $New_Offset;

	$Cdrec .= $FileName;

	// 保存压缩目录信息
	$GLOBALS["Ctrl_Dir"][] = $Cdrec;
}

//增加压缩目录
function PHPZip_AddDir($DirName){
	if (@file_exists($DirName)){
		if(!is_array($DirName)){
			$DirHead = opendir($DirName);
			while($Files = readdir($DirHead)){
				switch (true){
					case ($Files == "."):
						break;
					case ($Files == ".."):
						break;
					case (is_dir($DirName."/".$Files)):
						PHPZip_AddDir($DirName."/".$Files);
						break;
					default:
						$FileName    = $DirName."/".$Files;
						$FileHead    = fopen($FileName,"r");
						$FileContent = fread($FileHead,filesize($FileName)+1);
						fclose($FileHead);
						PHPZip_AddFile($FileContent,$FileName);
						break;
				}
			}
			closedir($DirHead);
		}
	}
}

//创建Zip文件
function PHPZip_CreatZipFile($ZipFileName){
	//获取ZIP数据
	$FileContent = PHPZip_GetZipData();
	//建立ZIP文件
	$FileHead    = fopen($ZipFileName,"wb");
	fwrite($FileHead,$FileContent,strlen($FileContent));
	fclose($FileHead);
}


//获取压缩内容列表
function PHPZip_GetList($ZipName){
	$Zip= @fopen($ZipName,"rb");
	if(!$Zip){ return(0); }
	$Centd = _ReadCentralDir($Zip,$ZipName);

	@rewind($Zip);
	@fseek($Zip,$Centd["offset"]);

	for ($i=0;$i<$Centd["entries"];$i++){
		$Headers = _ReadCentralFileHeaders($Zip);

		$Headers["index"]        = $i;
		$Info["filename"]        = $Headers["filename"];
		$Info["stored_filename"] = $Headers["stored_filename"];
		$Info["size"]            = $Headers["size"];
		$Info["compressed_size"] = $Headers["compressed_size"];
		$Info["crc"]             = strtoupper(dechex($Headers["crc"]));
		$Info["mtime"]           = $Headers["mtime"];
		$Info["comment"]         = $Headers["comment"];
		$Info["folder"]          = ($Headers["external"]==0x41FF0010 or $Headers["external"]==16)?1:0;
		$Info["index"]           = $Headers["index"];
		$Info["status"]          = $Headers["status"];
		$Ret[]                   = $Info;
	}
	return $Ret;
}

//获取zip数据
function PHPZip_GetZipData(){
	$Data     = implode("",$GLOBALS["DataSec"]);
	$CtrlDir  = implode("",$GLOBALS["Ctrl_Dir"]);
	$DirSize  = sizeof($GLOBALS["Ctrl_Dir"]);

	return $Data.$CtrlDir.$GLOBALS["Eof_Ctrl_Dir"].pack("v",$DirSize).pack("v",$DirSize).pack("V",strlen($CtrlDir)).pack("V",strlen($Data))."\x00\x00";
}

//解压Zip文件
function PHPZip_ExtractZipFile($ZipName,$ToDir="",$Index=Array(-1)){
	$OK  = 0;
	$Zip = @fopen($ZipName,"rb");

	if(!$Zip){ return(-1); }

	$CDir      = _ReadCentralDir($Zip,$ZipName);
	$Pos_Entry = $CDir["offset"];

	if(!is_array($Index)){ $Index = array($Index);  }
	for($i=0; $Index[$i];$i++){
		if(intval($Index[$i])!=$Index[$i] or $Index[$i]>$CDir["entries"])
		return(-1);
	}

	for($i=0; $i<$CDir["entries"]; $i++){
		@fseek($Zip, $Pos_Entry);
		$Headers          = _ReadCentralFileHeaders($Zip);
		$Headers["index"] = $i;
		$Pos_Entry        = ftell($Zip);

		@rewind($Zip);
		fseek($Zip, $Headers["offset"]);

		if(in_array("-1",$Index) or in_array($i,$Index)){
			$Stat[$Headers["filename"]] = _ExtractFile($Headers,$ToDir,$Zip);
		}
	}

	fclose($Zip);
	return $Stat;
}
?>